#pragma once

#include "Serializable.h"
#include <boost/thread/once.hpp>

#include "DataTypes.h"
#include "AbstractDataFieldIO.h"

#include <windows.h>

namespace events {


class EventFactory;


/**
 * A base class to all Event objects
 */
class BasicEvent : public platform::ISerializable
{
public:
	/**
	 * The type of the event
	 */
	enum EventTypes {
		EVENT_FILE_IO,
		EVENT_MEMORY,
		EVENT_THREADS,
		EVENT_MISC,
		EVENT_NETWORK,
		EVENT_WINDOWS,
		EVENT_SYNC,

		// special type of event, used
		// to finalize the file
		EVENT_EOF,

		_num_EventTypes
	};

	/** Constructor, takes the time stamp */
	explicit BasicEvent(EventTypes etype)
		: _etype(etype)
	{ _timestamp = ::GetTickCount(); }

	/** @return the type fo current event */
	inline EventTypes getType(void) const { return _etype; }

	/** @return timestamp of the event */
	inline DWORD getTickCount(void) const { return _timestamp(); }

	/** saves current event to stream */
	void toStream(std::ostream &os);

	/** reads next event from the stream */
	static BasicEvent * fromStream(std::istream &is);

	/** registers allocator objects for all event types*/
	static void RegisterEventAllocators(EventFactory & ef);

protected:
	/** @return the number of bytes that are written as event header */
	static inline unsigned int GetEventHeaderLength(void) { return (sizeof(EventTypes) + sizeof(DWORD)); }
public:
	virtual ~BasicEvent(void) {}

private:
	EventTypes _etype;
	GenericDataType<DWORD> _timestamp;




	static HANDLE _hHeap;
public:
	static inline void * operator new(size_t size) { return ::HeapAlloc(_hHeap, 0, size); }
	static inline void operator delete(void *p, size_t size) { ::HeapFree(_hHeap, 0, p); }
};


/**
 * Factory for "BasicEvent" objects
 */
class EventFactory {
public:
	struct AbstractAllocator {
		virtual BasicEvent * operator()() = 0;
	};

	/**
	 * This class implements Abstract Allocator
	 * interface for generic event
	 */
	template<typename EVENT>
	struct EVENTAllocator : public AbstractAllocator {
		virtual BasicEvent * operator()() {
			return static_cast<BasicEvent*>(new EVENT());
		}
	};


private:
	struct NullAllocator : public AbstractAllocator {
		virtual BasicEvent * operator()() { return NULL; }
	};

public:
	// the class is singleton
	static inline EventFactory & GetInstance(void);

	inline void registerAllocator(BasicEvent::EventTypes etype, AbstractAllocator *al) {
		delete _allocators[etype];
		_allocators[etype] = al;
	}

	template<typename EVENT>
	inline void registerAllocator(BasicEvent::EventTypes etype)
	{ registerAllocator(etype, new EVENTAllocator<EVENT>()); }

	inline BasicEvent * allocate(BasicEvent::EventTypes etype) {
		return (*_allocators[etype])();
	}


public:

private:
	// allocators
	AbstractAllocator * _allocators[BasicEvent::_num_EventTypes];

	// managing singleton instance
	static EventFactory *		_instance;
	static boost::once_flag		 _instance_flag;
	static inline void CreateInstance(void) { _instance = new EventFactory(); BasicEvent::RegisterEventAllocators(*_instance); }
	EventFactory(void);	
};


inline EventFactory & EventFactory::GetInstance(void) {
	boost::call_once(CreateInstance, _instance_flag);
	return *_instance;
}

}  // end namespace


inline std::ostream & operator<<(std::ostream &os, events::BasicEvent &be) {
	be.toStream(os);
	return os;
}